using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using LFloatMath.Math;
using NetProtocol.POD;

namespace IQIGame.Onigao.GamePlay
{
    public class BattleManager : Singleton<BattleManager>
    {
        private BattleManager()
        {
        }

        private BattleRenderContrller _mCtrl;
        public BattleRenderContrller ctrl => this._mCtrl;

        public void EnterBattle(BattleEnterPOD battleEnterPod)
        {
            if (this._mCtrl == null)
            {
                this.Initialize(battleEnterPod).Forget();
            }
            else
            {
                this.Restart(battleEnterPod).Forget();
            }
        }

        private async UniTaskVoid Initialize(BattleEnterPOD battleEnterPod)
        {
            LoadingModule.Instance.StartLoading(LoadingModule.LoadingType.Battle);

            this._mCtrl = new BattleRenderContrller();
            await this._mCtrl.Initialize(battleEnterPod);
            BattleEffectManager.Instance.Enable();
            // 正式开始战斗
            CBattleLogicModule.NetCore.CS_ReadyToStart().Forget();
            ManagerCenter.Render.OnEnterBattle();
            ManagerCenter.MonoDriver.updateEvent += Update;
            ManagerCenter.MonoDriver.lateUpdateEvent += LateUpdate;
        }

        private async UniTaskVoid Restart(BattleEnterPOD battleEnterPod)
        {
            LoadingModule.Instance.StartLoading(LoadingModule.LoadingType.Battle);
            await this._mCtrl.Restart(battleEnterPod);
            // 正式开始战斗
            CBattleLogicModule.NetCore.CS_ReadyToStart().Forget();
        }
        
        public void BattleStartNow()
        {
            LoadingModule.Instance.EndLoading();
        }

        private void Update(float deltaTime, float unScaledDeltaTime)
        { 
            // 在调用dispose对ManagerCenter.update-=之后，仍然会有一帧的调用，判空处理
            if (this.ctrl != null)
            {
                this.ctrl.Update(deltaTime);
                BattleEffectManager.Instance.Update(deltaTime, unScaledDeltaTime);
            }
        }

        private void LateUpdate()
        {
            this.ctrl?.LateUpdate();
        }

        public void Cast(BattleSkillPOD rPOD)
        {
            if (rPOD.skillId > 0)
            {
                var rUnit = this.ctrl.data.GetUnit(rPOD.unitId);
                var rSkillComp = rUnit.GetComponent<SkillViewComponent>(ETComponentType.SkillViewComponent);
                rSkillComp?.OnCast(rPOD.skillId);
            }

            CBattleLogicModule.NetCore.CS_CastSkill(rPOD).Forget();
        }

        public void SkillComplete()
        {
            CBattleLogicModule.NetCore.CS_CastComplete();
        }

        public async UniTask<bool> AutoBattle(bool bAuto)
        {
            var rBattleAutoPOD = new BattleAutoPOD()
            {
                isOpen = bAuto,
                curTempPos = LVector2.zero.ToPOD(),
            };
            if (this._mCtrl.queueCtrl.InWaitCastSkill(out var rWait))
            {
                var rCurWaitUnit = this._mCtrl.data.GetUnit(rWait.executerID);
                if (rCurWaitUnit != null)
                {
                    var rViewComp = rCurWaitUnit.GetComponent<UnitViewComponent>(ETComponentType.UnitViewComponent);
                    rBattleAutoPOD.curTempPos = rViewComp.position.ToLVector2XZ().ToPOD();
                    rBattleAutoPOD.curTempAspect = rViewComp.forward.ToLVector2XZ().ToPOD();
                }
            }
            var rResp = await CBattleLogicModule.NetCore.CS_AutoBattle(rBattleAutoPOD);
            if (rResp.IsSuccess)
            {
                this.ctrl.data.autoBattle = rResp.open;
                return this.ctrl.data.autoBattle;
            }
            return false;
        }

        public void Substitute(int oldUnit)
        {
            CBattleLogicModule.NetCore.CS_Substitute(oldUnit);
        }

        public void Skip()
        {
            CBattleLogicModule.NetCore.CS_SkipBattle();
        }

        public void ExitBattle()
        {
            ManagerCenter.MonoDriver.updateEvent -= Update;
            ManagerCenter.MonoDriver.lateUpdateEvent -= LateUpdate;
            BattleEffectManager.Instance.StopAll();
            BattleEffectManager.Instance.Disable();
            this.ctrl.Dispose();
            this._mCtrl = null;
            MsgDispatcher.Broadcast(MsgEventType.BattleView_Exit);
            ManagerCenter.Render.OnExitBattle();
            EffectReuseGlobal.Clear();
        }

        public void Dispose()
        {
            if (this.ctrl != null)
            {
                this.ExitBattle();
            }
        }
    }
}